package com.micro.ai.gateway.filter;

import com.micro.ai.commons.constant.ErrorCodeEnum;
import com.micro.ai.gateway.config.RateLimitConfig;
import com.micro.ai.gateway.util.IpUtils;
import com.micro.ai.gateway.util.ResponseUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.List;

/**
 * 限流过滤器（优化版）
 * 基于Redis的分布式限流
 * 
 * 优化点：
 * 1. 使用IpUtils统一IP获取逻辑
 * 2. 使用ResponseUtils统一错误响应
 * 3. 使用RateLimitConfig支持从Nacos动态刷新配置
 * 
 * @author micro-ai
 * @since 0.0.1
 * @version 1.3 支持动态刷新
 */
@Slf4j
@Component
public class RateLimitFilter implements GlobalFilter, Ordered {

    @Autowired
    private ReactiveRedisTemplate<String, String> redisTemplate;

    @Autowired
    private RateLimitConfig rateLimitConfig;

    // 限流配置
    private static final String RATE_LIMIT_PREFIX = "gateway:rate_limit:";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        
        // 检查是否在白名单中
        if (isWhitelisted(path)) {
            return chain.filter(exchange);
        }
        
        // 获取限流键
        String rateLimitKey = getRateLimitKey(request);
        
        return checkRateLimit(rateLimitKey)
                .flatMap(allowed -> {
                    if (allowed) {
                        return chain.filter(exchange);
                    } else {
                        return ResponseUtils.tooManyRequests(exchange, ErrorCodeEnum.B0004.getCode(), "请求过于频繁，请稍后再试");
                    }
                })
                .onErrorResume(throwable -> {
                    log.error("限流检查失败: {}", throwable.getMessage(), throwable);
                    // 限流检查失败时，允许请求通过，避免影响业务
                    return chain.filter(exchange);
                });
    }

    @Override
    public int getOrder() {
        return -800; // 限流过滤器优先级
    }

    /**
     * 检查是否在白名单中（支持动态刷新）
     */
    private boolean isWhitelisted(String path) {
        List<String> whitelistPaths = rateLimitConfig.getWhitelistPaths();
        if (whitelistPaths == null || whitelistPaths.isEmpty()) {
            return false;
        }
        
        for (String whitelistPath : whitelistPaths) {
            if (path.startsWith(whitelistPath)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 获取限流键
     */
    private String getRateLimitKey(ServerHttpRequest request) {
        String clientIp = IpUtils.getClientIp(request);
        String userId = request.getHeaders().getFirst("X-User-Id");
        String path = request.getURI().getPath();
        
        // 优先使用用户ID，其次使用IP
        String identifier = userId != null ? "user:" + userId : "ip:" + clientIp;
        return RATE_LIMIT_PREFIX + identifier + ":" + path;
    }

    /**
     * 检查限流
     */
    private Mono<Boolean> checkRateLimit(String key) {
        return redisTemplate.opsForValue().get(key)
                .flatMap(currentCount -> {
                    if (currentCount == null) {
                        // 第一次请求，设置计数器和过期时间
                        return redisTemplate.opsForValue()
                                .set(key, "1", Duration.ofSeconds(rateLimitConfig.getDefaultWindow()))
                                .then(Mono.just(true));
                    } else {
                        int count = Integer.parseInt(currentCount);
                        int limit = rateLimitConfig.getDefaultLimit();
                        if (count >= limit) {
                            return Mono.just(false);
                        } else {
                            // 增加计数
                            return redisTemplate.opsForValue()
                                    .increment(key)
                                    .map(newCount -> newCount <= limit);
                        }
                    }
                })
                .switchIfEmpty(Mono.just(true));
    }
}
